﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PIT.Labs.ProjectRepository.Owl2DotNet;
using PIT.Labs.ProjectRepository.Owl2DotNet.Rdf;
using Microsoft.SharePoint;
using System.Xml.Linq;
using Microsoft.SharePoint.Taxonomy;

namespace PIT.Labs.ProjectRepository.OntologyFeature
{
    /// <summary>
    /// Helper class that manages ontologies based on different <see cref="SPFile"/>s
    /// </summary>
    public static class OntologyHelper
    {
        static Dictionary<Guid, IOntology> ontologyStore;

        static OntologyHelper(){
            ontologyStore = new Dictionary<Guid, IOntology>();
        }

        /// <summary>
        /// Get the ontology that was parsed from the file with the given guid. If the file was not parsed yet, it will be parsed and is then cached for later calls.
        /// </summary>
        /// <param name="ontologyFileId">Guid of the file that contains the ontology</param>
        /// <returns>the parsed ontology</returns>
        public static IOntology GetOntology(Guid ontologyFileId)
        {
            if(!ontologyStore.ContainsKey(ontologyFileId)){
                SPWeb web = SPContext.Current.Web;
                SPList ontoFileList = web.Lists["OntologyXMLFiles"];
                SPFile ontoFile;
                try
                {
                    ontoFile = ontoFileList.Items[ontologyFileId].File;
                }catch{
                    //XXX: Log Error, more specific catch!
                    return null;
                }

                ontologyStore[ontologyFileId] = (new RdfXmlParser()).Load(ontoFile.OpenBinaryStream());
            }
            return ontologyStore[ontologyFileId];
        }

        /// <summary>
        /// Remove a parsed ontology from the cache
        /// </summary>
        /// <param name="ontologyFileId">Guid of the file that the ontology was parsed from</param>
        public static void ClearOntology(Guid ontologyFileId)
        {
            if (ontologyStore.ContainsKey(ontologyFileId))
            {
                ontologyStore.Remove(ontologyFileId);
            }
        }

        /// <summary>
        /// Serialize an <see cref="SPList"/> contents as individuals from an ontology.
        /// </summary>
        /// <param name="list">The list that provides individuals for the ontology</param>
        /// <param name="ontology">The ontology that the individuals will be placed into</param>
        /// <returns>RDF/OWL XML representation of the ontology containing the individuals from the given list</returns>
        public static XDocument SerializeList(SPList list, IOntology ontology)
        {
            // TODO: Refector Method
            var rdfOntology = ontology as RdfOntology;
            // Make sure the ontology is an rdf ontology. If not abort serialization process.
            if (rdfOntology != null)
            {
                // insert all the nodes from the ontology
                XDocument doc = new XDocument(rdfOntology.Node.Document);

                doc.Root.Add(new XComment("/////// exported individuals from " + SPContext.Current.Site.MakeFullUrl(list.DefaultViewUrl) + " ///////"));

                XNamespace local = doc.Root.Attributes().Where(x => ontology.Uri.OriginalString.Contains(x.Value)).LastOrDefault().Value;

                // loop through items in the list to add them to the xml doc
                foreach (SPListItem item in list.Items)
                {
                    XElement individual = SerializeListItemToOwlXml(ontology, local, item);
                    doc.Root.Add(individual);
                }

                return doc;
            }
            else
            {
                return XDocument.Parse("<error>ontology format not supported, need RDF</error>");
            }
        }

        private static XElement SerializeListItemToOwlXml(IOntology ontology, XNamespace local, SPListItem item)
        {
            XNamespace owl = "http://www.w3.org/2002/07/owl#";
            XNamespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

            XElement namedIndividual = new XElement(owl + "NamedIndividual");
            namedIndividual.SetAttributeValue(rdf + "about", SPContext.Current.Site.MakeFullUrl(item.Url));

            // look for every field if it contains information for the ontologies
            foreach (SPField field in item.Fields)
            {
                OntologyLookupField ol = field as OntologyLookupField;
                OntologyDataField od = field as OntologyDataField;
                TaxonomyField tf = field as TaxonomyField;
                if (ol != null)
                {
                    FillOwlNodeFromOntologyLookupField(namedIndividual, local, item, ol, ontology);
                }
                else if (od != null)
                {
                    FillOwlNodeFromOntologyDataField(namedIndividual, local, item, od, ontology);
                }
                else if (tf != null)
                {
                    FillOwlNodeFromTaxonomyField(namedIndividual, item, tf);
                }
            }
            return namedIndividual;
        }

        private static void FillOwlNodeFromTaxonomyField(XElement namedIndividual, SPListItem item, TaxonomyField tf)
        {
            XNamespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

            TaxonomyFieldValue val = item[tf.Id] as TaxonomyFieldValue;
            if (val != null)
            {
                TaxonomySession ts = new TaxonomySession(SPContext.Current.Site);
                Term term = ts.GetTerm(new Guid(val.TermGuid));

                var classUri = term.CustomProperties["ontology::uri"];
                if (classUri != null)
                {
                    XElement elem = new XElement(rdf + "type");
                    elem.SetAttributeValue(rdf + "resource", classUri);
                    namedIndividual.Add(elem);
                }
            }
        }

        private static void FillOwlNodeFromOntologyDataField(XElement namedIndividual, XNamespace local, SPListItem item, OntologyDataField od, IOntology ontology)
        {

            object val = item[od.Id];
            if (val != null)
            {
                var prop = ontology.GetDataProperty(od.PropertyUri);
                XName name = local + prop.Uri.OriginalString.Split('/', '#').Last();
                XElement elem = new XElement(name, val);
                namedIndividual.Add(elem);
            }
        }

        private static void FillOwlNodeFromOntologyLookupField(XElement namedIndividual, XNamespace local, SPListItem item, OntologyLookupField ol, IOntology ontology)
        {
            XNamespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

            try
            {
                object val = item[ol.Id];
                if (val != null)
                {
                    string targetUri = ((string)val).Split(';')[2].Trim();
                    if (!String.IsNullOrEmpty(targetUri))
                    {
                        var prop = ontology.GetObjectProperty(ol.PropertyUri);
                        XName name = local + prop.Uri.OriginalString.Split('/', '#').Last();
                        XElement elem = new XElement(name);
                        elem.SetAttributeValue(rdf + "resource", targetUri);
                        namedIndividual.Add(elem);
                    }
                }
            }
            catch {
                //XXX: Add Logging!
            }
        }
    }
}

